#include <std.h>
#include <system.h> 
#include <pio.h>
#include <pit.h>
#include <error.h>
#include <dint.h>
#include <irq.h>
#include <sched.h>
#include <asm.h>
#include <pgtrans.h>
#include <text.h>
#include <apm.h>

#define PIT_INIT_COMMAND 0x36 

void halt_sys();


void show_ivt(void *start,int entries)
{
	u16 *ivt,i=0;
	for(ivt=(u16 *)start;ivt<(entries*4);ivt+=2)	{
		printk("\nINT %xh  %x:[%x]",i,*ivt,*(ivt+1));
		i++;
	}
}

void setclock(unsigned long hz)
{
    unsigned short divisor = PIT_HZ / hz;
	outb(PIT_CONTROL, PIT_INIT_COMMAND);
	outportw(PIT_CNTR0, divisor);
}

#define INITFLAGS 0x212
volatile CPU_state _proc_fooler;

void halt_cpu();

void cl();
void thread_return()
{
	delete_thread(running_thread_id());
	mdelay(1000);
	error("Failed to end %d",running_thread_id());
	while(1);
}

/*Critical protections for below two functions have been provided in the functions in sched.c
which use these functions*/

int load_thread(CPU_state *state)
{
	_proc_fooler.eax=state->eax; 
	_proc_fooler.ebx=state->ebx;
	_proc_fooler.ecx=state->ecx;
	_proc_fooler.edx=state->edx;
	_proc_fooler.esi=state->esi;
	_proc_fooler.edi=state->edi;
	_proc_fooler.fs=state->fs;
	_proc_fooler.gs=state->gs;
	_proc_fooler.es=state->es;
	_proc_fooler.ds=state->ds;
	_proc_fooler.esp=state->esp;
	_proc_fooler.ebp=state->ebp;
	_proc_fooler.flags=state->flags;
	return NULL;
}

int save_thread(CPU_state *state)
{
	state->eax=_proc_fooler.eax;
	state->ebx=_proc_fooler.ebx;
	state->ecx=_proc_fooler.ecx;
	state->edx=_proc_fooler.edx;
	state->esi=_proc_fooler.esi;
	state->edi=_proc_fooler.edi;
	state->fs=_proc_fooler.fs;
	state->gs=_proc_fooler.gs;
	state->es=_proc_fooler.es;
	state->ds=_proc_fooler.ds;
	state->esp=_proc_fooler.esp;
	state->ebp=_proc_fooler.ebp; 
	state->flags=_proc_fooler.flags;
	return NULL;
}

#define SYS_DATA_SEL 0x10
	
int init_cpu_state(CPU_state *state)
{
	state->eax=NULL;
	state->ebx=NULL;
	state->ecx=NULL;
	state->edx=NULL;
	state->esi=NULL;
	state->edi=NULL;
	state->fs=SYS_DATA_SEL;
	state->gs=SYS_DATA_SEL;
	state->es=SYS_DATA_SEL;
	state->ds=SYS_DATA_SEL;
	state->esp=NULL;
	state->ebp=NULL;
	state->flags=INITFLAGS;
	return NULL;
}

void show_state(CPU_state *state)
{
	printk("\nState: eax=%x ebx=%x ecx=%x edx=%x esi=%x edi=%x esp=%x ebp=%x eflags=%x",state->eax,state->ebx,state->ecx,state->edx,state->esi,state->edi,state->esp,state->ebp,state->flags);
}

/*This function is a part of init_thread... initiating the thread descriptor is
safe... the thread has not been dispatched yet...*/
int set_stack(struct threadstate *state,void *entry,unsigned long stack,void *args,unsigned long signal)
{
	state->cpustate.esp=__set_stack(stack,(u32)entry,INITFLAGS,(u32)args,(u32)signal);
	state->cpustate.ebp=state->cpustate.esp;
	return NULL;
}	

/*Deprecated port thingies*/
unsigned char inportb(unsigned short _port)
{
  unsigned char result;
  __asm__  ("in %%dx, %%al" : "=a" (result) : "d" (_port));
  return result;
}

void outportb(unsigned short _port, unsigned char _data)
{
  __asm__ ("out %%al, %%dx" : :"a" (_data), "d" (_port));
}

void outportw(unsigned short _port,unsigned short _data)
{
	outb(_port,(unsigned char)_data);
	outb(_port,(unsigned char)(_data>>8));
}

/*It is not allowed to use these functions cli and sti directly... llock() and llockfree() can be used*/

inline void cli(void)
{
	__asm__ ("cli");
}

inline void sti(void)
{
	__asm__ ("sti");
}	

void __cpu(void)
{
	_getcpu();
	error("::\neax:%x ebx:%x ecx:%x edx:%x esp:%x ebp:%x esi:%x edi:%x",_cpu_eax,_cpu_ebx,_cpu_ecx,_cpu_edx,_cpu_esp,_cpu_ebp,_cpu_esi,_cpu_edi);
	error("\ncs:%x ds:%x ss:%x fs:%x gs:%x es:%x\neflags:%x",_cpu_cs,_cpu_ds,_cpu_ss,_cpu_fs,_cpu_gs,_cpu_es,_cpu_eflags);
	error("\nCR0:%x CR3:%x",_cpu_cr0,_cpu_cr3);
}


void _default_interrupt()
{
	printk("\nUnknown interrupt");
}

void exception(char *string)
{
	cli();
	if(scheduler_up())	{
		error("%s generated by::%s",string,running_thread_name());
		show_state((CPU_state *)currentthread());
		delete_thread(running_thread_id());
	} else	{
		printk("\nInit exception:%s",string);
		__cpu();
		suspend_all();
	}
	sti();
	while(1);
}

void _ex0(void)
{
	exception("\nDivision by zero");
}

void _ex1(void)
{
	exception("\nDebug exc");
}

void _ex2(void)
{
	exception("\nNMI (Non maskable Interrupt)");
}

void _ex3(void)
{
	exception("\nBreakpoint");
}

void _ex4(void)
{
	exception("\nINTO overflow");
}

void _ex5(void)
{
	exception("\nOut of Bounds exc");
}

void _ex6(void)
{
	exception("\nInvalid opcodes the");
}

void _ex7(void)
{
	exception("\nNo cothreador");
}

void _ex8(void)
{
	exception("\nDouble fault");
}

void _ex9(void)
{
	exception("\nCothreador segment overrun");
}

void _ex10(void)
{
	exception("\nBad TSS");
}

void _ex11(void)
{
	exception("\nSegment not present");
}

void _ex12(void)
{
	exception("\nStack fault");
}

void _ex13(void)
{
	exception("\nGeneral protection fault");
}

void _ex14(void)
{
	exception("\nPage fault");
}

void _ex15(void)
{
	exception("\nUnknown interrupt (on exc)");
}

void _ex16(void)
{
	exception("\nCothreador fault");
}

void _ex17(void)
{
	exception("\nAlignment check");
}

void _ex18(void)
{
	exception("\nMachine check");
}


/*The panic function... Before dying show the user what i was...*/
void suspend_all()
{
	cli();
	error("\n\nSystem halted-- Please reboot/poweroff!");
	halt_cpu();
}

void set_page_dir(unsigned long pgdiraddr)
{
	_write_cr3(pgdiraddr);
	_reload_pg();
}

void enable_pg()
{
	_write_cr0(_read_cr0() | 0x80000000);
	_reload_pg();
}

void disable_pg()
{
	_write_cr0(_read_cr0() & 0x7FFFFFFF);
	_write_cr3(0);
}

extern void _reload_tables();
void _restore_pm()
{
	_reload_tables();
	restore_irq_pm();
	setclock(SYSTEM_SCHEDULE_CLOCK_SPEED);
	restore_pg();
	llockfree();
}

void init_biosi(void)
{
	printk("\nBios call initiating");
	_load_lmembm();
	_bioscall(0x80,0,0,0,0,0,0x11);
}

void _prepare_rm()
{
	llock();
	prepare_irq_rm();
	setclock(18);
	disable_pg();
}

void halt_cpu()
{
	cli();
	asm("hlt");
}

void halt_sys()
{
	kdrv *sys_apm;
	sys_apm=get_device("apm");
	if(sys_apm)
		sys_apm->write_device(WRITE_APM_POWEROFF,0,0,0);
	cli();
	asm("hlt");
	while(1);
}

void idle()
{
	printk("\n<idle>");
	if(!_interrupt_flag())
		stop("Idling with interrupts disabled!");
	while(1)
		asm("hlt");
}

void halt()
{
	llock();	/*BN dies... :( */
	end_all_threades();
	halt_sys();
	stop("\nThis is no man's land.. Your APM failed to power off..");
}

int cmd_halt(struct param_list *params)
{
	halt();
}

int cmd_idle(struct param_list *params)
{
	idle();
}

void init_system()
{
	register_cmd("halt",cmd_halt,0,"Poweroff PC. Halts if no APM found");
	register_cmd("idle",cmd_idle,0,"Idles this process permanently");
}
